﻿#include "HelloWorldScene.h"

#include "GnsClientFrame.h"
#include <assert.h>

using namespace cocostudio::timeline;
using namespace ui;

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();
    
    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

HelloWorld::~HelloWorld()
{
	if (_writeToRepositoryThread.joinable())
	{
		_writeToRepositoryThread.join();
	}

	if (_readFromRepositoryThread.joinable())
	{
		_readFromRepositoryThread.join();
	}

	int iTmp = 0;
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    /**  you can create scene with following comment code instead of using csb file.
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.

    // add a "close" icon to exit the progress. it's an autorelease object
    auto closeItem = MenuItemImage::create(
                                           "CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
	closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                                origin.y + closeItem->getContentSize().height/2));

    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);

    /////////////////////////////
    // 3. add your codes below...

    // add a label shows "Hello World"
    // create and initialize a label
    
    auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);
    
    // position the label on the center of the screen
    label->setPosition(Vec2(origin.x + visibleSize.width/2,
                            origin.y + visibleSize.height - label->getContentSize().height));

    // add the label as a child to this layer
    this->addChild(label, 1);

    // add "HelloWorld" splash screen"
    auto sprite = Sprite::create("HelloWorld.png");

    // position the sprite on the center of the screen
    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

    // add the sprite as a child to this layer
    this->addChild(sprite, 0);
    **/
    
    //////////////////////////////
	

    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    auto rootNode = CSLoader::createNode("MainScene.csb");
	m_pRootNode = rootNode;

    addChild(rootNode);

	/////////////////////////////////////////////////////////

	// 注册同步事件监听器
	addCustomEventListener();

#if 1
	// 定时器
	this->schedule(schedule_selector(HelloWorld::OnTimer01), 0.05f);

	// 事件桥梁定时器
	this->schedule(schedule_selector(HelloWorld::OnEventBridge), 0.08f);
#endif

	//--- 按钮 -------------------------------------------
	auto pBt1 = static_cast<Button*>(rootNode->getChildByName("BT1"));
	pBt1->addTouchEventListener(CC_CALLBACK_2(HelloWorld::OnBt1, this));

	auto pBt2 = static_cast<Button*>(rootNode->getChildByName("BT2"));
	pBt2->addTouchEventListener(CC_CALLBACK_2(HelloWorld::OnBt2, this));

	auto pBt3 = static_cast<Button*>(rootNode->getChildByName("BT3"));
	pBt3->addTouchEventListener(CC_CALLBACK_2(HelloWorld::OnBt3, this));

	auto pBt4 = static_cast<Button*>(rootNode->getChildByName("BT4"));
	pBt4->addTouchEventListener(CC_CALLBACK_2(HelloWorld::OnBt4, this));

	auto pBt5 = static_cast<Button*>(rootNode->getChildByName("BT5"));
	pBt5->addTouchEventListener(CC_CALLBACK_2(HelloWorld::OnBt5, this));

	auto pBt6 = static_cast<Button*>(rootNode->getChildByName("BT6"));
	pBt6->addTouchEventListener(CC_CALLBACK_2(HelloWorld::OnBt6, this));

	auto pBt7 = static_cast<Button*>(rootNode->getChildByName("BT7"));
	pBt7->addTouchEventListener(CC_CALLBACK_2(HelloWorld::OnBt7, this));

	auto pBt8 = static_cast<Button*>(rootNode->getChildByName("BT8"));
	pBt8->addTouchEventListener(CC_CALLBACK_2(HelloWorld::OnBt8, this));

	auto pBt9 = static_cast<Button*>(rootNode->getChildByName("BT9"));
	pBt9->addTouchEventListener(CC_CALLBACK_2(HelloWorld::OnBt9, this));



    return true;
}

//void HelloWorld::onExit()
//{
//	int iTmp = 0;
//}

// 进入场景后，要初始化内容
void HelloWorld::onEnterTransitionDidFinish()
{
	int iTmp = 0;

	// this->onExit
	// this->_onEnterCallback
}

// 离开场景前，要释放内容
void HelloWorld::onExitTransitionDidStart()
{
	int iTmp = 0;
}

// 线程，向数据仓库写入数据，用于压力测试
void HelloWorld::threadWriteToRepository()
{
	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	assert(pGnsClientFrame);
	IDataRepository*	 pDataRepository = pGnsClientFrame->_pDataRepository;
	assert(pDataRepository);

	int iCount = 0;
	while (iCount++ < THREAD_MAX_LOOP)
	{
		std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_SLEEP_IN_LOOP));		// 毫秒级睡眠

		TEST_USER_INFO info;

		info.bClientPrint = true;					// 客户端打印
		info.bClientResend = true;				// 客户端收到后回发

		info.bServerPrint = true;					// 服务端打印
		info.bserverResend = true;				// 服务端收到后回发

		info.iClientResendElapse = 500;	// 客户端 回发时的睡眠间隔时间，ms
		info.iServerResendElapse = 600;	// 服务端 回发时的睡眠间隔时间，ms

		info.iResendMaxCount = 1000;		// 收到后回发的最大次数

		info.iCount = iCount++;
		std::sprintf(info.szName, "Zhang San");

		MSG_VALUE::STATE  iState = MSG_VALUE::DS_WRITE_FINISH;

		int iMainCode = 10;
		int iSubCode = 100;
		int iT3Code = 8;
		pDataRepository->writeToRepository(iMainCode, iSubCode, iT3Code, iState, (char*)(&info), sizeof(info));

		int iTmp = 0;
	}

	log("exit  threadWriteToRepository");
}

// 线程，从数据仓库中读取数据，用于压力测试
void HelloWorld::threadReadFromRepository()
{
	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	assert(pGnsClientFrame);
	IDataRepository*	 pDataRepository = pGnsClientFrame->_pDataRepository;
	assert(pDataRepository);

	int iCount = 0;
	while (iCount++ < THREAD_MAX_LOOP)
	{
		std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_SLEEP_IN_LOOP));		// 毫秒级睡眠

		int iMainCode = 10;
		int iSubCode = 100;
		int iT3Code = 8;

		MSG_VALUE msgValue;	// 使用完毕一定要执行 releaseVecRecord 进行释放

		// 从数据仓库中读取数据
#if 1
		pDataRepository->readFromRepository(iMainCode, iSubCode, iT3Code, msgValue);			// 试试这一句清除数据的
#else
		pDataRepository->readFromRepository(iMainCode, iSubCode, iT3Code, msgValue, false);	// 试试这一句不清数据的
#endif

		int iSize = msgValue.vecRecord.size();	// 测试用

		for (int i = 0; i < (int)msgValue.vecRecord.size(); i++)	// 遍历容器中的元素
		{
			MSG_VALUE::RECORD& e = msgValue.vecRecord[i];

			assert(sizeof(TEST_USER_INFO) == e.iDataLen);
			if (sizeof(TEST_USER_INFO) == e.iDataLen)
			{
				TEST_USER_INFO* pInfo = (TEST_USER_INFO*)e.pData;

				//log("%d  %s  state=%d ", pInfo->iCount, pInfo->szName, msgValue.state);
				auto pText01 = static_cast<Text*>(m_pRootNode->getChildByName("Text01"));

				std::string  strTmp = StringUtils::format("%d  %s  state=%d ", pInfo->iCount, pInfo->szName, msgValue.state);
				pText01->setString(strTmp);
			}
		}

		msgValue.releaseVecRecord();		// 释放 vecRecord 中的数据
	}
	
	log("exit  threadReadFromRepository");
}

// 事件桥梁定时器的回调函数
void HelloWorld::OnEventBridge(float dt)
{
	IEventBridge::EVENT_INFO eventInfo;

	while (IEventBridge::getInstance()->popFront(eventInfo))
	{
		log("%d  %d  %d  %s",
			eventInfo.iMainCode,
			eventInfo.iSubCode,
			eventInfo.iBodyLen,
			eventInfo.szBody
			);

		int iTmp = 0;
	}
}

// 定时器的回调函数
void HelloWorld::OnTimer01(float dt)
{
	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	assert(pGnsClientFrame);
	IDataRepository*	 pDataRepository = pGnsClientFrame->_pDataRepository;
	assert(pDataRepository);

	if (1)	// 从数据仓库中读取数据
	{
		int iMainCode = 10;
		int iSubCode = 100;
		int iT3Code = 8;

		MSG_VALUE msgValue;	// 使用完毕一定要执行 releaseVecRecord 进行释放

		// 从数据仓库中读取数据
		#if 0
			pDataRepository->readFromRepository(iMainCode, iSubCode, iT3Code, msgValue, true);			// 试试这一句清除数据的
		#else
			pDataRepository->readFromRepository(iMainCode, iSubCode, iT3Code, msgValue, false);	// 试试这一句不清数据的
		#endif

		int iSize = msgValue.vecRecord.size();	// 测试用

		for (int i = 0; i < (int)msgValue.vecRecord.size(); i++)	// 遍历容器中的元素
		{
			MSG_VALUE::RECORD& e = msgValue.vecRecord[i];

			assert(sizeof(TEST_USER_INFO) == e.iDataLen);
			if (sizeof(TEST_USER_INFO) == e.iDataLen)
			{
				TEST_USER_INFO* pInfo = (TEST_USER_INFO*)e.pData;

				auto pText01 = static_cast<Text*>(m_pRootNode->getChildByName("Text01"));

				std::string  strTmp = StringUtils::format("%d  %s  state=%d ", pInfo->iCount, pInfo->szName, msgValue.state);
				pText01->setString(strTmp);

			}
		}

		msgValue.releaseVecRecord();		// 释放 vecRecord 中的数据
	}
}

void HelloWorld::OnBt1(Ref * pSender, ui::Widget::TouchEventType type)
{
	//auto rootNode = CSLoader::createNode("MainScene.csb");    // 这种写法是错的，他又会建立一个 rootNode，并非 HelloWorld::init() 里面的 rootNode

	if (type == ui::Widget::TouchEventType::ENDED)
	{
		int iClientId = 0;

		if (1)	// 连接服务端
		{
			bool bConnect = GnsClientFrame::getInstance()->isClientSocketValid(iClientId);

			if (bConnect)	// 连接状态
			{
				GnsClientFrame::getInstance()->closeSocket(iClientId);		// 手工断线
			}
			else  // 断线状态
			{
				GnsClientFrame::getInstance()->manualConnect(iClientId);	// 手动连接
			}
		}

		if (0)
		{
			auto pShowScreen = static_cast<Text*>(m_pRootNode->getChildByName("ShowScreen"));
			std::string strTmp = "this is a tree.  \n i am a man.";
			pShowScreen->setString(strTmp);

			std::string strOther = pShowScreen->getString();	// 获得原来的内容

			std::string str03 = strOther + "\n cocos2d";
			pShowScreen->setString(str03);		// 追加内容

			int iTmp = 0;
		}

		if (0)	// 这样是没问题的，因为 MY_EVENT_MSG_01 与本函数都在主线程操作
		{
			static int iCount = 1;

			TEST_USER_INFO userInof;
			std::sprintf (userInof.szName, "this is a tree.");
		}

		if (1)	// 启一个线程来发消息，这样发送会崩溃，说明主线程受不了其它线程投递自定义消息的冲击
		{
			std::thread	 _coreWorkerThread = std::thread(&HelloWorld::sendCustomEvent, this);;	// 发送自定义消息
			_coreWorkerThread.detach();
		}
	}
}

// 发送自定义消息
// 用线程这样发送会崩溃，说明主线程受不了其它线程投递自定义消息的冲击
// 会出现“CCEventDispatcher 1215  CCASSERT(_inDispatch == 1, "_inDispatch should be 1 here.")” 这样的断言，
// 点【忽略】不理这个崩溃就OK啦
void HelloWorld::sendCustomEvent()
{
	static int iCount = 1;
#if 0
	TEST_USER_INFO userInof;
	std::sprintf(userInof.szName, "this is a tree.");

	for (int i = 0; i < 2000; i++)
	{
		TRANS_TO_SCENE_MSG*  pTT = new TRANS_TO_SCENE_MSG;

		pTT->bRelease = true;
		pTT->_mainCode = 10;
		pTT->_subCode = 101;
		pTT->_dataLen = 0;

		userInof.iCount = iCount++;   // 已经在下面的循环内赋值啦

		int iLen = GnsClientFrame::min(TRANS_TO_SCENE_MSG::DATA_MAX_LEN, sizeof(userInof));
		std::memcpy(pTT->_data, (char*)(&userInof), iLen);

		cocos2d::Director::getInstance()->getEventDispatcher()->dispatchCustomEvent(MY_EVENT_MSG_01, pTT);

		//std::this_thread::sleep_for(std::chrono::milliseconds(1));	// 睡一下就不会崩溃啦，好奇葩???
	}
#endif
}

void HelloWorld::OnBt2(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type != ui::Widget::TouchEventType::ENDED)
		return;

	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	IDataRepository*	 pDataRepository = pGnsClientFrame->_pDataRepository;
	int iRes = 0;

	if (1)	// 向数据仓库写入数据
	{
		static int iCount = 0;
		const int iFinish = 3;

		if (iCount > iFinish)		// 数量超出指定范围，不许写
			return;

		TEST_USER_INFO info;
	
		info.bClientPrint = true;					// 客户端打印
		info.bClientResend = true;				// 客户端收到后回发

		info.bServerPrint = true;					// 服务端打印
		info.bserverResend = true;				// 服务端收到后回发

		info.iClientResendElapse = 500;	// 客户端 回发时的睡眠间隔时间，ms
		info.iServerResendElapse = 600;	// 服务端 回发时的睡眠间隔时间，ms

		info.iResendMaxCount = 1000;		// 收到后回发的最大次数

		info.iCount = iCount++;
		std::sprintf ( info.szName, "Zhang San")  ;

		MSG_VALUE::STATE  iState = MSG_VALUE::DS_WRITEING;

		if (iCount >= iFinish)
			iState = MSG_VALUE::DS_WRITE_FINISH;

		int iMainCode = 10;
		int iSubCode = 100; 
		int iT3Code = 8;
		iRes = pDataRepository->writeToRepository(iMainCode, iSubCode, iT3Code, iState, (char*)(&info), sizeof(info));
	}
}

void HelloWorld::OnBt3(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type != ui::Widget::TouchEventType::ENDED)
		return;

	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	IDataRepository*	 pDataRepository = pGnsClientFrame->_pDataRepository;
	int iRes = 0;

	int iMainCode = 10;
	int iSubCode = 100;
	int iT3Code = 8;

	if (0)	// 测试用，OK的
	{
		// 向数据仓库写入状态，功能是OK的
		iRes = pDataRepository->setStateToRepository(iMainCode, iSubCode, iT3Code, MSG_VALUE::DS_NO);
	}

	if (0)	// 测试用，清空数据仓库中指定 Key 的 MSG_VALUE 值，OK的
	{
		//iRes = pDataRepository->clearFromRepository(iMainCode, iSubCode, iT3Code, true);			// 将容器内的 Key 和 MSG_VALUE 都移除
		iRes = pDataRepository->clearFromRepository(iMainCode, iSubCode, iT3Code, false);		// 保留Key，但清除了 MSG_VALUE 中的记录值并初始化MSG_VALUE中的几个参数
	}

	if (0) // 测试用，OK的
	{
		// 清空数据仓库中所有的数据
		iRes = pDataRepository->clearAllData();
	}

	if (1)	// 从数据仓库中读取数据
	{
		MSG_VALUE msgValue;	// 使用完毕一定要执行 releaseVecRecord 进行释放

		// 从数据仓库中读取数据
		#if 0
			iRes = pDataRepository->readFromRepository(iMainCode, iSubCode, iT3Code, msgValue);			// 试试这一句清除数据的
		#else
			iRes = pDataRepository->readFromRepository(iMainCode, iSubCode, iT3Code, msgValue, false);	// 试试这一句不清数据的
		#endif
		
		int iSize = msgValue.vecRecord.size();	// 测试用

		for (int i = 0; i < (int)msgValue.vecRecord.size(); i++)	// 遍历容器中的元素
		{
			MSG_VALUE::RECORD& e = msgValue.vecRecord[i];

			assert(sizeof(TEST_USER_INFO) == e.iDataLen);
			if (sizeof(TEST_USER_INFO) == e.iDataLen)
			{
				TEST_USER_INFO* pInfo = (TEST_USER_INFO*)e.pData;

				log("%d  %s  state=%d ", pInfo->iCount, pInfo->szName, msgValue.state);
			}
		}

		msgValue.releaseVecRecord();		// 释放 vecRecord 中的数据
	}
}

void HelloWorld::OnBt4(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type != ui::Widget::TouchEventType::ENDED)
		return;

	CMemBlockPool2* pMsgPool = GnsClientFrame::getInstance()->_pMsgPool;
	assert(pMsgPool);

	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	assert(pGnsClientFrame);

	if (1)	// 单条记录发送
	{
		int iMainCode = MAIN_TEST_USER_INFO;
		int iSubCode = SUB_TEST_USER_INFO_01;	// 显示用户信息	

		TEST_USER_INFO info;
		info.bClientPrint = true;					// 客户端打印
		info.bClientResend = true;				// 客户端收到后回发

		info.bServerPrint = true;				// 服务端打印
		info.bserverResend = true;			// 服务端收到后回发

		info.iClientResendElapse = 0;		// 客户端 回发时的睡眠间隔时间，ms
		info.iServerResendElapse = 0;	//  3000;	// 服务端 回发时的睡眠间隔时间，ms

		info.iResendMaxCount = 200000;
		info.iCount = 1;
		std::sprintf(info.szName, "this is a tree.");

		pGnsClientFrame->sendMsg(0, iMainCode, iSubCode, (char*)(&info), sizeof(info));
		pGnsClientFrame->sendMsg(1, iMainCode, iSubCode, (char*)(&info), sizeof(info));
	}
}

void HelloWorld::OnBt5(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type != ui::Widget::TouchEventType::ENDED)
		return;

	CMemBlockPool2* pMsgPool = GnsClientFrame::getInstance()->_pMsgPool;
	assert(pMsgPool);

	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	assert(pGnsClientFrame);

	int iMainCode = MAIN_TEST_USER_INFO;
	int iSubCode = SUB_TEST_USER_INFO_01;	// 显示用户信息	

	TEST_USER_INFO info;
	info.bClientPrint = true;					// 客户端打印
	info.bClientResend = true;				// 客户端收到后回发

	info.bServerPrint = true;				// 服务端打印
	info.bserverResend = false;			// 服务端收到后回发

	info.iClientResendElapse = 0;		// 客户端 回发时的睡眠间隔时间，ms
	info.iServerResendElapse = 0;	//  3000;	// 服务端 回发时的睡眠间隔时间，ms

	info.iResendMaxCount = 200000;
	info.iCount = 1;
	std::sprintf(info.szName, "this is a tree.");

	const int iMaxLoop = 100;

	for (int i = 0; i < iMaxLoop; i++)
	{
		pGnsClientFrame->sendMsg(0, iMainCode, iSubCode, (char*)(&info), sizeof(info));

		info.iCount++;
	}
}

void HelloWorld::OnBt6(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type != ui::Widget::TouchEventType::ENDED)
		return;

	// 杀掉定时器
	this->unschedule(schedule_selector(HelloWorld::OnTimer01));

	// 线程，向数据仓库写入数据，用于压力测试
	_writeToRepositoryThread = std::thread(&HelloWorld::threadWriteToRepository, this);

	// 线程，从数据仓库中读取数据，用于压力测试
	_readFromRepositoryThread = std::thread(&HelloWorld::threadReadFromRepository, this);
}

void HelloWorld::OnBt7(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type == ui::Widget::TouchEventType::ENDED)
	{
		auto pButton = (Button*)pSender;
		int BtnTag = pButton->getTag();
		log("this is tag ==%d", BtnTag);
	}
}

void HelloWorld::OnBt8(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type == ui::Widget::TouchEventType::ENDED)
	{
		auto pButton = (Button*)pSender;
		int BtnTag = pButton->getTag();
		log("this is tag ==%d", BtnTag);
	}
}

void HelloWorld::OnBt9(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type == ui::Widget::TouchEventType::ENDED)
	{
		auto pButton = (Button*)pSender;
		int BtnTag = pButton->getTag();
		log("this is tag ==%d", BtnTag);
	}
}

// 注册同步事件监听器
void HelloWorld::addCustomEventListener()
{
	// 注册同步事件监听器
	cocos2d::Director::getInstance()->getEventDispatcher()->addCustomEventListener(MY_EVENT_MSG_01, CC_CALLBACK_1(HelloWorld::OnMyEventCustom01, this));
	cocos2d::Director::getInstance()->getEventDispatcher()->addCustomEventListener(MY_EVENT_MSG_02, CC_CALLBACK_1(HelloWorld::OnMyEventCustom02, this));

}

// 当服务端密集发送消息时，或者由线程密集发送消息时，
// 会出现“CCEventDispatcher 1215  CCASSERT(_inDispatch == 1, "_inDispatch should be 1 here.")” 这样的崩溃，
// 点【忽略】不理这个崩溃就OK啦
void HelloWorld::OnMyEventCustom01(EventCustom* event)
{
	CLIENT_CORE_MSG* pCore = (CLIENT_CORE_MSG*)(event->getUserData());
	assert(sizeof(TEST_USER_INFO) == pCore->iBodyLen);
	if (sizeof(TEST_USER_INFO) != pCore->iBodyLen)
		return;

	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();

	TEST_USER_INFO*  pInfo = (TEST_USER_INFO*)(pCore->pBody);
	
	if (pInfo->bClientPrint)
	{
		log("%d  %s  GnsClientFrame::OnLogicMsg  ",
			pInfo->iCount,
			pInfo->szName
			);
	}

	pInfo->iCount++;

	if (pInfo->bClientResend && pInfo->iCount <= pInfo->iResendMaxCount)
	{
		pGnsClientFrame->sendMsg(pCore->iClientId, pCore->iMainCode, pCore->iSubCode, (char*)pInfo, sizeof(TEST_USER_INFO));

		if (pInfo->iClientResendElapse > 0)
			std::this_thread::sleep_for(std::chrono::milliseconds(pInfo->iClientResendElapse));		// 毫秒级睡眠
	}
}

void HelloWorld::OnMyEventCustom02(EventCustom* event)
{
	log("OnMyEventCustom02");

}
